package net.gdface.sdk.thrift;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import net.gdface.sdk.CodeInfo;
import net.gdface.sdk.FRect;
import net.gdface.sdk.FaceApi;
import net.gdface.thrift.TypeTransformer;

import java.io.IOException;
import com.google.common.base.Function;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.microsoft.thrifty.ThriftException;
import com.microsoft.thrifty.service.AsyncClientBase;
import com.microsoft.thrifty.service.ServiceMethodCallback;

import net.gdface.thrift.ClientFactory;
import static com.google.common.base.Preconditions.*;

/**
 * 基于thrift/swift框架生成的client端代码提供{@link FaceApi}接口的异步RPC实现(线程安全)<br>
 * 转发所有{@link FaceApi}接口方法到{@link net.gdface.sdk.thrift.client.FaceApiClient}实例<br>
 * 所有服务端抛出的{@link RuntimeException}异常被封装到{@link ServiceRuntimeException}中抛出<br>
 * Example:<br>
 * <pre>
 *        FaceApiThriftClientAsync thriftInstance = ClientFactory
 *            .builder()
 *            .setHostAndPort("127.0.0.1",26413)
 *            .build(FaceApiThriftClientAsync.class);
 * </pre>
 * 计算机生成代码(generated by automated tools ThriftServiceDecoratorGenerator @author guyadong)<br>
 * @author guyadong
 *
 */
public class FaceApiThriftClientAsync {
    private final ClientFactory factory;

    public ClientFactory getFactory() {
        return factory;
    }
    public FaceApiThriftClientAsync(ClientFactory factory) {
        super();
        this.factory = checkNotNull(factory,"factory is null");
    }
    private class MethodCallback<L,R> implements ServiceMethodCallback<R>{
	    private final AsyncClientBase.Listener closeListener = new AsyncClientBase.Listener(){
	        @Override
	        public void onTransportClosed() {
	        }
	
	        @Override
	        public void onError(Throwable error) {
	            MethodCallback.this.onError(error);
	        }
	    };
        final net.gdface.sdk.thrift.client.FaceApiClient service = factory.applyInstance(net.gdface.sdk.thrift.client.FaceApiClient.class,closeListener);
        final SettableFuture<L> feature = SettableFuture.create();
        final Function<R,L> transformer;

        MethodCallback(Function<R, L> transformer) {
            this.transformer = transformer;
        }

        @Override
        public void onSuccess(R result) {
            feature.set(transformer.apply(result));            
            try {
                service.close();
            } catch (IOException e) {
            }
        }

        @Override 
        public void onError(Throwable error) {            
            if(error instanceof ThriftException ){
                if(((ThriftException)error).kind == ThriftException.Kind.MISSING_RESULT  ){
                    this.onSuccess(null);
                }
            }
            feature.setException(error);
            try {
                service.close();
            } catch (IOException e) {
            }
        }
    }
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("FaceApiThriftClientAsync [factory=");
        builder.append(factory);
        builder.append(",interface=");
        builder.append(FaceApi.class.getName());
        builder.append("]");
        return builder.toString();
    }
    /**
     * 默认的{@link FutureCallback}实现
     * @author guyadong
     *
     * @param <V>
     */
    public static class DefaultCallback<V> implements FutureCallback<V>{
        @Override
        public void onSuccess(V result) {
            // DO NOTHING
        }
        @Override
        public void onFailure(Throwable t) {
            try{
                throw t;
            }
            catch(net.gdface.sdk.thrift.client.ImageErrorException e){
                onImageErrorException(e);
            }
            catch(net.gdface.sdk.thrift.client.NotFaceDetectedException e){
                onNotFaceDetectedException(e);
            }
            catch(net.gdface.sdk.thrift.client.ServiceRuntimeException e){
                onServiceRuntimeException(e);
            }
            catch(Throwable e){
                onThrowable(e);
            }
        }
        protected void onImageErrorException(net.gdface.sdk.thrift.client.ImageErrorException e){
            System.out.println(e.serviceStackTraceMessage);
        }
        protected void onNotFaceDetectedException(net.gdface.sdk.thrift.client.NotFaceDetectedException e){
            System.out.println(e.serviceStackTraceMessage);
        }
        protected void onServiceRuntimeException(net.gdface.sdk.thrift.client.ServiceRuntimeException e){
            System.out.println(e.serviceStackTraceMessage);
        }
        protected void onThrowable(Throwable e){
            e.printStackTrace();
        }
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#compare2Face(byte[],net.gdface.sdk.CodeInfo,byte[],net.gdface.sdk.CodeInfo)}
     */
    public ListenableFuture<Double> compare2Face(byte[] imgData1,
        CodeInfo facePos1,
        byte[] imgData2,
        CodeInfo facePos2){
        MethodCallback<Double,Double> nativeCallback = 
            new MethodCallback<Double,Double>(
                new Function<Double,Double>() {
                        @Override
                        public Double apply(Double input) {
                            return input;
                }});
        nativeCallback.service.compare2Face(
                TypeTransformer.getInstance().to(
                    imgData1,
                    byte[].class,
                    okio.ByteString.class),
            TypeTransformer.getInstance().to(
                    facePos1,
                    CodeInfo.class,
                    net.gdface.sdk.thrift.client.CodeInfo.class),
            TypeTransformer.getInstance().to(
                    imgData2,
                    byte[].class,
                    okio.ByteString.class),
            TypeTransformer.getInstance().to(
                    facePos2,
                    CodeInfo.class,
                    net.gdface.sdk.thrift.client.CodeInfo.class),nativeCallback);
        return nativeCallback.feature;
    }
    public void compare2Face(byte[] imgData1,
        CodeInfo facePos1,
        byte[] imgData2,
        CodeInfo facePos2,
        FutureCallback<Double>callback){
        factory.addCallback(compare2Face(imgData1,facePos1,imgData2,facePos2), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#compareCode(byte[],byte[])}
     */
    public ListenableFuture<Double> compareCode(byte[] code1,
        byte[] code2){
        MethodCallback<Double,Double> nativeCallback = 
            new MethodCallback<Double,Double>(
                new Function<Double,Double>() {
                        @Override
                        public Double apply(Double input) {
                            return input;
                }});
        nativeCallback.service.compareCode(
                TypeTransformer.getInstance().to(
                    code1,
                    byte[].class,
                    okio.ByteString.class),
            TypeTransformer.getInstance().to(
                    code2,
                    byte[].class,
                    okio.ByteString.class),nativeCallback);
        return nativeCallback.feature;
    }
    public void compareCode(byte[] code1,
        byte[] code2,
        FutureCallback<Double>callback){
        factory.addCallback(compareCode(code1,code2), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#compareCodes(byte[],net.gdface.sdk.CodeInfo[])}
     */
    public ListenableFuture<double[]> compareCodes(byte[] code1,
        CodeInfo[] codes){
        MethodCallback<double[],java.util.List<Double>> nativeCallback = 
            new MethodCallback<double[],java.util.List<Double>>(
                new Function<java.util.List<Double>,double[]>() {
                        @Override
                        public double[] apply(java.util.List<Double> input) {
                            return TypeTransformer.getInstance().todoubleArray(
                    input,
                    double.class,
                    double.class);
                }});
        nativeCallback.service.compareCodes(
                TypeTransformer.getInstance().to(
                    code1,
                    byte[].class,
                    okio.ByteString.class),
            TypeTransformer.getInstance().to(
                    codes,
                    CodeInfo.class,
                    net.gdface.sdk.thrift.client.CodeInfo.class),nativeCallback);
        return nativeCallback.feature;
    }
    public void compareCodes(byte[] code1,
        CodeInfo[] codes,
        FutureCallback<double[]>callback){
        factory.addCallback(compareCodes(code1,codes), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#compareFaces(byte[],byte[],int)}
     */
    public ListenableFuture<Map<CodeInfo, Double>> compareFaces(byte[] code,
        byte[] imgData,
        int faceNum){
        MethodCallback<Map<CodeInfo, Double>,Map<net.gdface.sdk.thrift.client.CodeInfo,Double>> nativeCallback = 
            new MethodCallback<Map<CodeInfo, Double>,Map<net.gdface.sdk.thrift.client.CodeInfo,Double>>(
                new Function<Map<net.gdface.sdk.thrift.client.CodeInfo,Double>,Map<CodeInfo, Double>>() {
                        @Override
                        public Map<CodeInfo, Double> apply(Map<net.gdface.sdk.thrift.client.CodeInfo,Double> input) {
                            return TypeTransformer.getInstance().to(
                    input,
                    net.gdface.sdk.thrift.client.CodeInfo.class,
                    Double.class,
                    CodeInfo.class,
                    Double.class);
                }});
        nativeCallback.service.compareFaces(
                TypeTransformer.getInstance().to(
                    code,
                    byte[].class,
                    okio.ByteString.class),
            TypeTransformer.getInstance().to(
                    imgData,
                    byte[].class,
                    okio.ByteString.class),
            faceNum,nativeCallback);
        return nativeCallback.feature;
    }
    public void compareFaces(byte[] code,
        byte[] imgData,
        int faceNum,
        FutureCallback<Map<CodeInfo, Double>>callback){
        factory.addCallback(compareFaces(code,imgData,faceNum), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#compareFeatures(byte[],java.util.List)}
     */
    public ListenableFuture<List<Double>> compareFeatures(byte[] code1,
        List<byte[]> codes){
        MethodCallback<List<Double>,List<Double>> nativeCallback = 
            new MethodCallback<List<Double>,List<Double>>(
                new Function<List<Double>,List<Double>>() {
                        @Override
                        public List<Double> apply(List<Double> input) {
                            return TypeTransformer.getInstance().to(
                    input,
                    Double.class,
                    Double.class);
                }});
        nativeCallback.service.compareFeatures(
                TypeTransformer.getInstance().to(
                    code1,
                    byte[].class,
                    okio.ByteString.class),
            TypeTransformer.getInstance().to(
                    codes,
                    byte[].class,
                    okio.ByteString.class),nativeCallback);
        return nativeCallback.feature;
    }
    public void compareFeatures(byte[] code1,
        List<byte[]> codes,
        FutureCallback<List<Double>>callback){
        factory.addCallback(compareFeatures(code1,codes), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#detectAndCompare2Face(byte[],net.gdface.sdk.FRect,byte[],net.gdface.sdk.FRect)}
     */
    public ListenableFuture<Double> detectAndCompare2Face(byte[] imgData1,
        FRect detectRect1,
        byte[] imgData2,
        FRect detectRect2){
        MethodCallback<Double,Double> nativeCallback = 
            new MethodCallback<Double,Double>(
                new Function<Double,Double>() {
                        @Override
                        public Double apply(Double input) {
                            return input;
                }});
        nativeCallback.service.detectAndCompare2Face(
                TypeTransformer.getInstance().to(
                    imgData1,
                    byte[].class,
                    okio.ByteString.class),
            TypeTransformer.getInstance().to(
                    detectRect1,
                    FRect.class,
                    net.gdface.sdk.thrift.client.FRect.class),
            TypeTransformer.getInstance().to(
                    imgData2,
                    byte[].class,
                    okio.ByteString.class),
            TypeTransformer.getInstance().to(
                    detectRect2,
                    FRect.class,
                    net.gdface.sdk.thrift.client.FRect.class),nativeCallback);
        return nativeCallback.feature;
    }
    public void detectAndCompare2Face(byte[] imgData1,
        FRect detectRect1,
        byte[] imgData2,
        FRect detectRect2,
        FutureCallback<Double>callback){
        factory.addCallback(detectAndCompare2Face(imgData1,detectRect1,imgData2,detectRect2), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#detectAndGetCodeInfo(byte[],int)}
     */
    public ListenableFuture<CodeInfo[]> detectAndGetCodeInfo(byte[] imgData,
        int faceNum){
        MethodCallback<CodeInfo[],java.util.List<net.gdface.sdk.thrift.client.CodeInfo>> nativeCallback = 
            new MethodCallback<CodeInfo[],java.util.List<net.gdface.sdk.thrift.client.CodeInfo>>(
                new Function<java.util.List<net.gdface.sdk.thrift.client.CodeInfo>,CodeInfo[]>() {
                        @Override
                        public CodeInfo[] apply(java.util.List<net.gdface.sdk.thrift.client.CodeInfo> input) {
                            return TypeTransformer.getInstance().toArray(
                    input,
                    net.gdface.sdk.thrift.client.CodeInfo.class,
                    CodeInfo.class);
                }});
        nativeCallback.service.detectAndGetCodeInfo(
                TypeTransformer.getInstance().to(
                    imgData,
                    byte[].class,
                    okio.ByteString.class),
            faceNum,nativeCallback);
        return nativeCallback.feature;
    }
    public void detectAndGetCodeInfo(byte[] imgData,
        int faceNum,
        FutureCallback<CodeInfo[]>callback){
        factory.addCallback(detectAndGetCodeInfo(imgData,faceNum), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#detectCenterFace(byte[])}
     */
    public ListenableFuture<CodeInfo> detectCenterFace(byte[] imgData){
        MethodCallback<CodeInfo,net.gdface.sdk.thrift.client.CodeInfo> nativeCallback = 
            new MethodCallback<CodeInfo,net.gdface.sdk.thrift.client.CodeInfo>(
                new Function<net.gdface.sdk.thrift.client.CodeInfo,CodeInfo>() {
                        @Override
                        public CodeInfo apply(net.gdface.sdk.thrift.client.CodeInfo input) {
                            return TypeTransformer.getInstance().to(
                    input,
                    net.gdface.sdk.thrift.client.CodeInfo.class,
                    CodeInfo.class);
                }});
        nativeCallback.service.detectCenterFace(
                TypeTransformer.getInstance().to(
                    imgData,
                    byte[].class,
                    okio.ByteString.class),nativeCallback);
        return nativeCallback.feature;
    }
    public void detectCenterFace(byte[] imgData,
        FutureCallback<CodeInfo>callback){
        factory.addCallback(detectCenterFace(imgData), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#detectFace(byte[])}
     */
    public ListenableFuture<CodeInfo[]> detectFace(byte[] imgData){
        MethodCallback<CodeInfo[],java.util.List<net.gdface.sdk.thrift.client.CodeInfo>> nativeCallback = 
            new MethodCallback<CodeInfo[],java.util.List<net.gdface.sdk.thrift.client.CodeInfo>>(
                new Function<java.util.List<net.gdface.sdk.thrift.client.CodeInfo>,CodeInfo[]>() {
                        @Override
                        public CodeInfo[] apply(java.util.List<net.gdface.sdk.thrift.client.CodeInfo> input) {
                            return TypeTransformer.getInstance().toArray(
                    input,
                    net.gdface.sdk.thrift.client.CodeInfo.class,
                    CodeInfo.class);
                }});
        nativeCallback.service.detectFace(
                TypeTransformer.getInstance().to(
                    imgData,
                    byte[].class,
                    okio.ByteString.class),nativeCallback);
        return nativeCallback.feature;
    }
    public void detectFace(byte[] imgData,
        FutureCallback<CodeInfo[]>callback){
        factory.addCallback(detectFace(imgData), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#getCodeInfo(byte[],int,net.gdface.sdk.CodeInfo[])}
     */
    public ListenableFuture<CodeInfo[]> getCodeInfo(byte[] imgData,
        int faceNum,
        CodeInfo[] facePos){
        MethodCallback<CodeInfo[],java.util.List<net.gdface.sdk.thrift.client.CodeInfo>> nativeCallback = 
            new MethodCallback<CodeInfo[],java.util.List<net.gdface.sdk.thrift.client.CodeInfo>>(
                new Function<java.util.List<net.gdface.sdk.thrift.client.CodeInfo>,CodeInfo[]>() {
                        @Override
                        public CodeInfo[] apply(java.util.List<net.gdface.sdk.thrift.client.CodeInfo> input) {
                            return TypeTransformer.getInstance().toArray(
                    input,
                    net.gdface.sdk.thrift.client.CodeInfo.class,
                    CodeInfo.class);
                }});
        nativeCallback.service.getCodeInfo(
                TypeTransformer.getInstance().to(
                    imgData,
                    byte[].class,
                    okio.ByteString.class),
            faceNum,
            TypeTransformer.getInstance().to(
                    facePos,
                    CodeInfo.class,
                    net.gdface.sdk.thrift.client.CodeInfo.class),nativeCallback);
        return nativeCallback.feature;
    }
    public void getCodeInfo(byte[] imgData,
        int faceNum,
        CodeInfo[] facePos,
        FutureCallback<CodeInfo[]>callback){
        factory.addCallback(getCodeInfo(imgData,faceNum,facePos), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#getCodeInfo(byte[],net.gdface.sdk.CodeInfo)}
     */
    public ListenableFuture<CodeInfo> getCodeInfo(byte[] imgData,
        CodeInfo facePos){
        MethodCallback<CodeInfo,net.gdface.sdk.thrift.client.CodeInfo> nativeCallback = 
            new MethodCallback<CodeInfo,net.gdface.sdk.thrift.client.CodeInfo>(
                new Function<net.gdface.sdk.thrift.client.CodeInfo,CodeInfo>() {
                        @Override
                        public CodeInfo apply(net.gdface.sdk.thrift.client.CodeInfo input) {
                            return TypeTransformer.getInstance().to(
                    input,
                    net.gdface.sdk.thrift.client.CodeInfo.class,
                    CodeInfo.class);
                }});
        nativeCallback.service.getCodeInfoSingle(
                TypeTransformer.getInstance().to(
                    imgData,
                    byte[].class,
                    okio.ByteString.class),
            TypeTransformer.getInstance().to(
                    facePos,
                    CodeInfo.class,
                    net.gdface.sdk.thrift.client.CodeInfo.class),nativeCallback);
        return nativeCallback.feature;
    }
    public void getCodeInfo(byte[] imgData,
        CodeInfo facePos,
        FutureCallback<CodeInfo>callback){
        factory.addCallback(getCodeInfo(imgData,facePos), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#getFeature(java.util.Map)}
     */
    public ListenableFuture<byte[]> getFeature(Map<ByteBuffer, CodeInfo> faces){
        MethodCallback<byte[],okio.ByteString> nativeCallback = 
            new MethodCallback<byte[],okio.ByteString>(
                new Function<okio.ByteString,byte[]>() {
                        @Override
                        public byte[] apply(okio.ByteString input) {
                            return TypeTransformer.getInstance().to(
                    input,
                    okio.ByteString.class,
                    byte[].class);
                }});
        nativeCallback.service.getFeature(
                TypeTransformer.getInstance().to(
                    faces,
                    ByteBuffer.class,
                    CodeInfo.class,
                    okio.ByteString.class,
                    net.gdface.sdk.thrift.client.CodeInfo.class),nativeCallback);
        return nativeCallback.feature;
    }
    public void getFeature(Map<ByteBuffer, CodeInfo> faces,
        FutureCallback<byte[]>callback){
        factory.addCallback(getFeature(faces), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#hasFace(byte[])}
     */
    public ListenableFuture<Boolean> hasFace(byte[] imgData){
        MethodCallback<Boolean,Boolean> nativeCallback = 
            new MethodCallback<Boolean,Boolean>(
                new Function<Boolean,Boolean>() {
                        @Override
                        public Boolean apply(Boolean input) {
                            return input;
                }});
        nativeCallback.service.hasFace(
                TypeTransformer.getInstance().to(
                    imgData,
                    byte[].class,
                    okio.ByteString.class),nativeCallback);
        return nativeCallback.feature;
    }
    public void hasFace(byte[] imgData,
        FutureCallback<Boolean>callback){
        factory.addCallback(hasFace(imgData), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#isLocal()}
     */
    public boolean isLocal(){
        return false;
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#multiFaceFeature()}
     */
    public ListenableFuture<Boolean> multiFaceFeature(){
        MethodCallback<Boolean,Boolean> nativeCallback = 
            new MethodCallback<Boolean,Boolean>(
                new Function<Boolean,Boolean>() {
                        @Override
                        public Boolean apply(Boolean input) {
                            return input;
                }});
        nativeCallback.service.multiFaceFeature(
                nativeCallback);
        return nativeCallback.feature;
    }
    public void multiFaceFeature(FutureCallback<Boolean>callback){
        factory.addCallback(multiFaceFeature(), callback);
    }
    /**
     * see also {@link net.gdface.sdk.FaceApi#sdkVersion()}
     */
    public ListenableFuture<String> sdkVersion(){
        MethodCallback<String,String> nativeCallback = 
            new MethodCallback<String,String>(
                new Function<String,String>() {
                        @Override
                        public String apply(String input) {
                            return input;
                }});
        nativeCallback.service.sdkVersion(
                nativeCallback);
        return nativeCallback.feature;
    }
    public void sdkVersion(FutureCallback<String>callback){
        factory.addCallback(sdkVersion(), callback);
    }
}